package golf.stockinfo.buysellsimulation;

import golf.stockinfo.buysellsimulation.SimulationData.TransactionType;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;

import org.joda.time.DateTime;
import org.joda.time.Days;

/**
 * This class is to simulation the approach of referring to Weekly FX rate from
 * E-finance as a indicator and use the indicator to trade in Forex market.
 * 
 * @author golf
 * 
 */
public class SimulateFOREX_CombinedApproach {
	
	public static String CURRENCY_PAIR = "EURUSD";
	
	public static int SMAV = 10;
	
	//The best cut loss is -13,000
	public static Date START_DATE = Date.valueOf("2011-09-04");
	public static int SIMULATION_LENGTH_IN_YEAR = 20;
	
	public static double FOREX_GEARING_RATIO = 20;
	public static double FOREX_LOT_SIZE = 100000;
	
	public static double LOSS_STOPPING_PER_LOT = -6000; //Unit of currency is the left side currenc of the pair.
	public static double PERCENT_MAX_LOSS_ON_BALANCE = -0.6; //percentage of affordable loss on the total remaining balance for the trading per transaction, 0.7 means 70%
	public static double INITIAL_CAPITAL = 13000; //Unit of currency is the left side currenc of the pair.
	public static double MINIMUM_RUNNING_BALANCE = 500; //minimum balance to quite the trading.
	public static double CAPPED_FOREX_LOT = 20;
	
	private static TreeMap<DateTime, MarketData> weeklyData;
//	private TreeMap<DateTime, MarketData> dailyData;
	private ArrayList<SimulationData> simulationDataArray = new ArrayList<SimulationData>();
	private static DecimalFormat numberFormat = new DecimalFormat("###,###.00");
	private static DecimalFormat interestFormat = new DecimalFormat("###,###.0000");
	private static DecimalFormat daysFormat = new DecimalFormat("###,###");
	
	public SimulateFOREX_CombinedApproach(){
		try {
			//Load Oracle JDBC driver
			DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
//			DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		} catch (SQLException e) {
			e.printStackTrace();
		}        
	}

	/**
	 * 
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		SimulateFOREX_CombinedApproach ths = new SimulateFOREX_CombinedApproach();
		ths.execute(true);
		
		System.out.println(ths.simulationDataArray.get(0).getStartDate() + ";" + ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate() + 
				";" + numberFormat.format(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance()));
		
		DateTime dt1 = new DateTime(ths.simulationDataArray.get(0).getStartDate());
		DateTime dt2 = new DateTime(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
		
		System.out.println("======================================================");
		System.out.println("Summary");
		System.out.println("======================================================");
		System.out.println("Investment start date      : " + ths.simulationDataArray.get(0).getStartDate());
		System.out.println("Investment end date        : " + ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
		System.out.println("Investment time (days)     : " + daysFormat.format(Days.daysBetween(dt1, dt2).getDays()));
		System.out.println("Start Investment amount    : " + numberFormat.format(INITIAL_CAPITAL));
		System.out.println("Total amount of end balance: " + numberFormat.format(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance()));
		String roundOffPercent = Double.toString((getApproxEquivalentInterestPerYear(
				INITIAL_CAPITAL, 
				ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance(), 
				Days.daysBetween(dt1, dt2).getDays())) * 100).split("\\.")[0];
		System.out.println("Approximate Interest/year  : " + roundOffPercent + "%");
		System.out.println("======================================================");

		/*
		 * Down here is commented out, they are for finding the best combination of all factors
		 */
		
//		SimulateFOREX_CombinedApproach ths = new SimulateFOREX_CombinedApproach();
//		Date[] dateArr = {Date.valueOf("2003-02-15"),
//				Date.valueOf("2003-06-15"),
//				Date.valueOf("2003-10-13"),
//				Date.valueOf("2004-02-10"),
//				Date.valueOf("2004-06-09"),
//				Date.valueOf("2004-10-07"),
//				Date.valueOf("2005-02-04"),
//				Date.valueOf("2005-06-04"),
//				Date.valueOf("2005-10-02"),
//				Date.valueOf("2006-01-30"),
//				Date.valueOf("2006-05-30"),
//				Date.valueOf("2006-09-27"),
//				Date.valueOf("2007-01-25"),
//				Date.valueOf("2007-05-25"),
//				Date.valueOf("2007-09-22"),
//				Date.valueOf("2008-01-20"),
//				Date.valueOf("2008-05-19"),
//				Date.valueOf("2008-09-16"),
//				Date.valueOf("2009-01-14"),
//				Date.valueOf("2009-05-14"),
//				Date.valueOf("2009-09-11"),
//				Date.valueOf("2010-01-09"),
//				Date.valueOf("2010-05-09"),
//				Date.valueOf("2010-09-06"),
//				Date.valueOf("2011-01-04"),
//				Date.valueOf("2011-05-04"),
//				Date.valueOf("2011-09-01"),
//				Date.valueOf("2011-12-30")};
//		
//		//Print header
//		System.out.print(
//				"CappedLot" + 
//				";MinBalance" +
//				";PercentMaxLossOnBal" +
//				";LossStopPerLot" +
//				";SMAV");
//		for (Date dt:dateArr){
//			System.out.print(";" + dt.toString());
//		}
//		System.out.println("");
//		
//		for(int cappedLot = 100; cappedLot >= 20; cappedLot = cappedLot - 20){
//			for(int smav = 8; smav <= 25; smav ++){
//				for(double minRunBal = 500; minRunBal <= 10000; minRunBal = minRunBal + 1000){
//					for(double pcntMaxLossOnBal = -0.4 ; pcntMaxLossOnBal >= -0.8; pcntMaxLossOnBal = pcntMaxLossOnBal - 0.2){
//						for(double lossStopPerLot = -1000; lossStopPerLot >= -7000; lossStopPerLot = lossStopPerLot - 1000){
//							ArrayList<SimulationData> sims = new ArrayList<SimulationData>();
//							for (Date dt:dateArr){
//								
//								SimulateFOREX_CombinedApproach.CAPPED_FOREX_LOT = cappedLot;
//								SimulateFOREX_CombinedApproach.START_DATE = dt;
//								SimulateFOREX_CombinedApproach.SMAV = smav;
//								SimulateFOREX_CombinedApproach.LOSS_STOPPING_PER_LOT = lossStopPerLot;
//								SimulateFOREX_CombinedApproach.PERCENT_MAX_LOSS_ON_BALANCE = pcntMaxLossOnBal;
//								SimulateFOREX_CombinedApproach.MINIMUM_RUNNING_BALANCE = minRunBal;
//								
//								if(dt.equals(Date.valueOf("2003-05-16"))
//										&& smav == 8
//										&& lossStopPerLot == -2500
//										&& pcntMaxLossOnBal < -0.7
//										&& minRunBal == 3000){
//									System.out.print("");
//								}
//								ths = new SimulateFOREX_CombinedApproach();
//
//								ths.execute(false);
//
//								SimulationData simDat = new SimulationData();
//								simDat.setStartDate(ths.simulationDataArray.get(0).getStartDate());
//								simDat.setEndDate(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
//								simDat.setBalance(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance());
////								simDat.setEndIndex(getApproxEquivalentInterestPerYear(INITIAL_CAPITAL, simDat.getBalance_asDouble(), Days.daysBetween(new DateTime(ths.simulationDataArray.get(0).getStartDate()), new DateTime(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate())).getDays()));
//								sims.add(simDat);
//							}
//							
//							System.out.print(
//									cappedLot + ";" + 
//									minRunBal + ";"
//									+ pcntMaxLossOnBal + ";"
//									+ lossStopPerLot + ";"
//									+ "SMAV-" + smav);
//							for(SimulationData sim:sims){
//								System.out.print(";" + numberFormat.format(sim.getBalance()));
////								System.out.print(";" + interestFormat.format(sim.getEndIndex()));
//							}
//							System.out.println("");
//						}
//					}
//				}
//			}
//		}

	}
	
	public void execute(boolean printDetailedSimulation){
		
		boolean isInvested = false;

		double runningBalance = INITIAL_CAPITAL;
		
        try {
			
        	String cacheKey = "jdbc:oracle:thin:@172.16.115.132:1521/finance:forex:forex";
        	Connection conn = (Connection) GlobalCache.getCache(cacheKey);
        	
        	if(conn == null){
                conn = DriverManager.getConnection(  
                        "jdbc:oracle:thin:@172.16.115.132:1521/finance", "forex", "forex");
                GlobalCache.addCache(cacheKey, conn);
        	}
        	
//            Connection conn = DriverManager.getConnection(  
//                    "jdbc:mysql://localhost:3306/test", "finance", "finance");
            conn.setAutoCommit(false);

//			if(weeklyData == null
//					|| (weeklyData != null && SimulateFOREX_CombinedApproach.SMAV != weeklyData.get(weeklyData.firstKey()).getSMAV())){
//				weeklyData = loadMarketDataFromDB(conn);
//			}
			weeklyData = loadMarketDataFromDB(conn);
			
			Iterator<DateTime> itrWeekly = weeklyData.keySet().iterator();
			MarketData thisWeek = null;
			
			SimulationData simulationItem = null;
			
			if(itrWeekly.hasNext()){
				thisWeek = weeklyData.get(itrWeekly.next());
			}

			DateTime stopDate = new DateTime(START_DATE).plusYears(SIMULATION_LENGTH_IN_YEAR).plusMonths(3);
			DateTime currDate = new DateTime(thisWeek.getDate());


			//Start looping through the weekly data to find a start-point, either to short or buy SET50
			while(itrWeekly.hasNext()
					&& currDate.isBefore(stopDate.getMillis())){
				
				while(itrWeekly.hasNext()
						&& (currDate.isBefore(START_DATE.getTime()) || currDate.isEqual(START_DATE.getTime()))){
					//Loop to the start point
					thisWeek = weeklyData.get(itrWeekly.next());
					currDate = new DateTime(thisWeek.getDate());
				}
				
				if( ! isInvested ){

					if(thisWeek.getHighIndex() < thisWeek.getCloseIndexSMAV()){
						//This means the candle stick is under the SMAV line
						simulationItem = new SimulationData();
						simulationItem.setTypeOfTransaction(TransactionType.SHORT_TEFX);
						
						isInvested = true;

					}else if(thisWeek.getLowIndex() > thisWeek.getCloseIndexSMAV()){
						//This means the candle stick is above the SMAV line
						simulationItem = new SimulationData();
						simulationItem.setTypeOfTransaction(TransactionType.LONG_TFEX);
						
						isInvested = true;

					}else{
						//This means the candle stick is in the middle of the line, need to skip run
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
					}
					
					if(isInvested){
						
						if(runningBalance < 500){
							System.out.print("");
						}
						
						simulationItem.setStartDate(thisWeek.getDate());
						simulationItem.setStartIndex(getForexClosingIndexOfWeek(thisWeek, conn));

						double cappedLossTotal = Math.abs(runningBalance * PERCENT_MAX_LOSS_ON_BALANCE);
						double marginAmountPerLot = FOREX_LOT_SIZE * simulationItem.getStartIndex() / FOREX_GEARING_RATIO;
						double numberOfLotToBuy = 0;
						if(marginAmountPerLot > Math.abs(LOSS_STOPPING_PER_LOT)){
							numberOfLotToBuy = cappedLossTotal / marginAmountPerLot;
						}else{
							numberOfLotToBuy = Math.abs(cappedLossTotal / LOSS_STOPPING_PER_LOT);
						}
						
						if(numberOfLotToBuy > 0.1){
							numberOfLotToBuy = roundOff(numberOfLotToBuy, 1);
						}else{
							numberOfLotToBuy = 0.1d;
						}
						
						if(numberOfLotToBuy > CAPPED_FOREX_LOT){
							numberOfLotToBuy = CAPPED_FOREX_LOT;
						}
						
						simulationItem.setNumberOfForexLot(numberOfLotToBuy);
						
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
					}
					
				}else{
					
					double maxLossPerLot = getMaxProfitOrLossPerLotDuringWeek(true, simulationItem, thisWeek, conn);
					double maxLoss = simulationItem.getNumberOfForexLot_asDouble() * maxLossPerLot;
					
					if(maxLoss < simulationItem.getMaximumPointLossForPeriod()){
						simulationItem.setMaximumPointLossForPeriod(maxLoss);
					}
					
					double maxGain = simulationItem.getNumberOfForexLot_asDouble()
							* getMaxProfitOrLossPerLotDuringWeek(false, simulationItem, thisWeek, conn);

					if(maxGain > simulationItem.getMaximumPointGainForPeriod()){
						simulationItem.setMaximumPointGainForPeriod(maxGain);
					}
					
					if(simulationItem.getCutLossAmount_asDouble() == 0
							&& maxLossPerLot < LOSS_STOPPING_PER_LOT){
						//Stop the loss
						simulationItem.setCutLossDate(thisWeek.getDate());
						simulationItem.setCutLossAmount(LOSS_STOPPING_PER_LOT * simulationItem.getNumberOfForexLot_asDouble());
						simulationItem.setBalance(runningBalance + (LOSS_STOPPING_PER_LOT * simulationItem.getNumberOfForexLot_asDouble()));
						runningBalance = runningBalance + (LOSS_STOPPING_PER_LOT * simulationItem.getNumberOfForexLot_asDouble());
					
					}else if(hasCutSMAV(simulationItem, thisWeek)){
						
						simulationItem.setEndDate(thisWeek.getDate());
						simulationItem.setEndIndex(getForexClosingIndexOfWeek(thisWeek, conn));
						
						if(simulationItem.getCutLossAmount_asDouble() == 0){
							simulationItem.setBalance( (runningBalance + (simulationItem.getNumberOfForexLot_asDouble() * getProfitOrLossPerLotAtEndOfWeek(simulationItem, thisWeek, conn))));
							runningBalance = (runningBalance + (simulationItem.getNumberOfForexLot_asDouble() * getProfitOrLossPerLotAtEndOfWeek(simulationItem, thisWeek, conn)));
						}
						simulationDataArray.add(simulationItem);
						isInvested = false;
					}else{
						
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
						
					}
					
					//Check if we should quite the market
					if(runningBalance < MINIMUM_RUNNING_BALANCE){
						if(isInvested){
							simulationDataArray.add(simulationItem);
						}
						break;
					}
					
				}
				
			}
			
			if(printDetailedSimulation){
				System.out.println(
						"StartDate;" +
						"StartIndex;" +
						"Type;" +
						"EndDate;" +
						"EndIndex;" +
						"LotToBuy;" +
						"MaxGain;" +
						"MaxLoss;" +
						"CutLossDate;" +
						"CutLossAmt;" +
						"Balance");
				for(SimulationData simulateItem:simulationDataArray){
					System.out.println(
							simulateItem.getStartDate() + ";" + 
							simulateItem.getStartIndex() + ";"
							+ simulateItem.getTypeOfTransaction() + ";"
							+ simulateItem.getEndDate() + ";" 
							+ simulateItem.getEndIndex() + ";" 
							+ simulateItem.getNumberOfForexLot() + ";"
							+ simulateItem.getMaximumPointGainForPeriod() + ";"
							+ simulateItem.getMaximumPointLossForPeriod() + ";"
							+ simulateItem.getCutLossDate() + ";" 
							+ numberFormat.format(simulateItem.getCutLossAmount()) + ";" 
							+ numberFormat.format(simulateItem.getBalance()));
				}
				
				System.out.println("================ Done ================");
			}
			
			conn.rollback();
//			conn.close();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	private boolean hasCutSMAV(SimulationData simulationItem, MarketData thisWeek) {
		
		if(simulationItem.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
			if(thisWeek.getHighIndex() < thisWeek.getCloseIndexSMAV()){
				return true;
			}
		}else if(simulationItem.getTypeOfTransaction().equals(TransactionType.SHORT_TEFX)){
			if(thisWeek.getLowIndex() > thisWeek.getCloseIndexSMAV()){
				return true;
			}
		}
		return false;
	}

	private double getClosingIndexFromForex(Date dt, Connection conn) throws SQLException{
		
		String cacheKey = "ClosingIndexFromForex" + dt.toString() + CURRENCY_PAIR + "FOREX";
		
		Double output = (Double) GlobalCache.getCache(cacheKey);
		
		if(output != null){
			return output;
		}
		
		PreparedStatement pstmt = conn.prepareStatement(
				"SELECT CLOSE" +
				"  FROM PRICE_HIST " +
				" WHERE TRADING_DATE = ?" +
				"   AND CURRENCY_PAIR = ?" +
				"   AND TRADING_TYPE = 'DAILY'" +
				"   AND MARKET_NAME = 'FOREX'");
		
		pstmt.setDate(1, dt);
		pstmt.setString(2, CURRENCY_PAIR);
		
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			output = rs.getDouble("CLOSE");
		}
		
		rs.close();
		pstmt.close();
		
		GlobalCache.addCache(cacheKey, output);
		return output;
		
	}
	/**
	 * 
	 * @param getLoss - true means to get max loss, otherwise get max profit
	 * @param simData
	 * @param thisWeek
	 * @param conn
	 * @return
	 * @throws SQLException
	 */
	private double getMaxProfitOrLossPerLotDuringWeek(boolean getLoss, SimulationData simData, MarketData thisWeek, Connection conn) throws SQLException{
		
		Double highestDuringWeek = null;
		Double lowestDuringWeek = null;
		
		double output = 0;
		
		String lowestCacheKey = "LowestDuringWeek" + thisWeek.getNextDate().toString() 
				+ thisWeek.getDate().toString() + CURRENCY_PAIR + "FOREX";
		
		lowestDuringWeek = (Double) GlobalCache.getCache(lowestCacheKey);
		
		String highestCacheKey = "HighestDuringWeek" + thisWeek.getNextDate().toString() 
				+ thisWeek.getDate().toString() + CURRENCY_PAIR + "FOREX";
		
		highestDuringWeek = (Double) GlobalCache.getCache(highestCacheKey);
		
		if(lowestDuringWeek == null
				|| highestDuringWeek == null){
			
			PreparedStatement pstmt = conn.prepareStatement(
					"SELECT MIN(LOW), MAX(HIGH)" +
					"  FROM PRICE_HIST " +
					" WHERE TRADING_DATE >= ?" +
					"   AND TRADING_DATE < ?" +
					"   AND CURRENCY_PAIR = ?" +
					"   AND TRADING_TYPE = 'DAILY'" +
					"   AND MARKET_NAME = 'FOREX'");
			
			pstmt.setDate(1, thisWeek.getDate());
			pstmt.setDate(2, thisWeek.getNextDate());
			pstmt.setString(3, CURRENCY_PAIR);
			
			ResultSet rs = pstmt.executeQuery();
			
			if(rs.next()){
				
				highestDuringWeek = rs.getDouble(2);
				lowestDuringWeek = rs.getDouble(1);
				
			}

			rs.close();
			pstmt.close();
			
			if(highestDuringWeek == 0 || lowestDuringWeek == 0){
				//Somthing is wrong, return 0
				return 0;
			}
			
			GlobalCache.addCache(highestCacheKey, highestDuringWeek);
			GlobalCache.addCache(lowestCacheKey, lowestDuringWeek);
			
		}
		
		if(getLoss){
			if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
				double percentLoss = (lowestDuringWeek - simData.getStartIndex()) / simData.getStartIndex();
				output = percentLoss * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
			}else{
				double percentLoss = (simData.getStartIndex() - highestDuringWeek) / simData.getStartIndex();
				output = percentLoss * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
			}
		}else{
			if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
				double percentGain = (highestDuringWeek - simData.getStartIndex()) / simData.getStartIndex();
				output = percentGain * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
			}else{
				double percentGain = (simData.getStartIndex() - lowestDuringWeek) / simData.getStartIndex();
				output = percentGain * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
			}
		}
		
		
		return output;
	}
	
	private double getForexClosingIndexOfWeek(MarketData thisWeek, Connection conn) throws SQLException{
		
		String cacheKey = "ForexClosingIndexOfWeek" + thisWeek.getDate().toString() + CURRENCY_PAIR + "FOREX";
		
		Double closeIndex = (Double) GlobalCache.getCache(cacheKey);
		
		if(closeIndex != null){
			return closeIndex;
		}
		
		PreparedStatement pstmt = conn.prepareStatement(
				"SELECT CLOSE" +
				"  FROM PRICE_HIST " +
				" WHERE TRADING_DATE = ( SELECT MAX(TRADING_DATE)" +
				"						   FROM PRICE_HIST" +
				"						  WHERE CURRENCY_PAIR = ?" +
				"						    AND MARKET_NAME = 'FOREX'" +
				"							AND TRADING_DATE < ?" +
				"							AND TRADING_TYPE = 'DAILY'" +
				"                      )" +
				"   AND CURRENCY_PAIR = ?" +
				"   AND TRADING_TYPE = 'DAILY'" +
				"   AND MARKET_NAME = 'FOREX'");
		
		pstmt.setString(1, CURRENCY_PAIR);
		/*
		 * This is to get 2 days before the next date in the result of SQL as a result of week period different in eFin and Forex market interval
		 */
		pstmt.setDate(2, new Date(new DateTime(thisWeek.getNextDate()).minusDays(1).getMillis())); 
		pstmt.setString(3, CURRENCY_PAIR);
		
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			closeIndex = rs.getDouble("CLOSE");
		}
		
		rs.close();
		pstmt.close();
		
		GlobalCache.addCache(cacheKey, closeIndex);
		return closeIndex;
	}
	
	private double getProfitOrLossPerLotAtEndOfWeek(SimulationData simData, MarketData thisWeek, Connection conn) throws SQLException{
		
		double output = 0;
		double closeIndex = getForexClosingIndexOfWeek(thisWeek, conn);

		if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
			double percentChange = (closeIndex - simData.getStartIndex()) / simData.getStartIndex();
			output = percentChange * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
		}else{
			double percentChange = (simData.getStartIndex() - closeIndex) / simData.getStartIndex();
			output = percentChange * simData.getStartIndex() * FOREX_LOT_SIZE; //Unit depends on the currency pair, e.g EURUSD will mean EUR
		}
		
		return output;
	}
	private TreeMap<DateTime, MarketData> loadMarketDataFromDB(Connection conn) throws SQLException{
		
		String cacheKey = "loadMarketDataFromDB" + CURRENCY_PAIR + "EFINWEEKLY" + SMAV;
		
		TreeMap<DateTime, MarketData> output = (TreeMap<DateTime, MarketData>) GlobalCache.getCache(cacheKey);
		
		if(output != null){
			return output;
		}else{
			output = new TreeMap<DateTime, MarketData>();
		}
		
        PreparedStatement pstmt = conn.prepareStatement(
        		"SELECT " +
        		"  TRADING_DATE" +
        		" ,OPEN" +
        		" ,HIGH" +
        		" ,LOW" +
        		" ,CLOSE " +
        		"FROM PRICE_HIST " +
        		"WHERE MARKET_NAME = 'EFIN' " +
        		"  AND CURRENCY_PAIR = ? " +
        		"  AND TRADING_TYPE = 'WEEKLY' " +
        		"ORDER BY TRADING_DATE");
        
        pstmt.setString(1, CURRENCY_PAIR);
        
        ResultSet rsWeekly = pstmt.executeQuery();
        
        MarketData prevMarketData = null;

		while (rsWeekly.next()) {
			
			MarketData marketData = new MarketData();
			
			DateTime date = new DateTime(rsWeekly.getDate("TRADING_DATE"));
			
			marketData.setDate(rsWeekly.getDate("TRADING_DATE"));
			marketData.setOpenIndex(rsWeekly.getFloat("OPEN"));
			marketData.setHighIndex(rsWeekly.getFloat("HIGH"));
			marketData.setLowIndex(rsWeekly.getFloat("LOW"));
			marketData.setCloseIndex(rsWeekly.getFloat("CLOSE"));
			marketData.setSMAV(SMAV);
			if(output.size() >= SMAV - 1){
				float sum = marketData.getCloseIndex();
				Iterator<DateTime> oItr = output.keySet().iterator();
				int i=0;
				while(oItr.hasNext()){
					i++;
					MarketData o = output.get(oItr.next());
					if(i > (output.size() - SMAV + 1)){
						sum = sum + o.getCloseIndex();
					}
				}
				marketData.setCloseIndexSMAV(sum/SMAV);
			}
			
			if(output.size() > 0){
				prevMarketData.setNextDate(marketData.getDate());
				prevMarketData = marketData;
			}
			
			prevMarketData = marketData;

			output.put(date, marketData);
		}
			
		rsWeekly.close();
		pstmt.close();
		
		//To make a mark of item to be clean up for those do not have SMAV for close index
		Iterator<DateTime> oItr = output.keySet().iterator();
		//This has to be defined to avoid ConcurrentModification Exception
		ArrayList<DateTime> tobeDeleted = new ArrayList<DateTime>();
		while(oItr.hasNext()){
			DateTime nextIndx = oItr.next();
			MarketData o = output.get(nextIndx);
			if(o.getCloseIndexSMAV() == 0){
				tobeDeleted.add(nextIndx);
			}
		}
		
		for(DateTime dt:tobeDeleted){
			//This is now really delete the item
			output.remove(dt);
		}
		
		GlobalCache.addCache(cacheKey, output);
		
		return output;
		
	}
	
	private static double getApproxEquivalentInterestPerYear(double initialCapital, double endedBalance, long days){
		
		int capPeriod = 180;
		double initInterest = 0.001f;
		double newEndedBalance = initialCapital;
		
		boolean reverseCalc = false;
		if(endedBalance < initialCapital){
			reverseCalc = true;
			double tmpFlt = initialCapital;
			initialCapital = endedBalance;
			endedBalance = tmpFlt;
		}
		
		while(Math.abs((endedBalance - newEndedBalance)/endedBalance) > 0.003){
			
			int i=1;
			newEndedBalance = initialCapital;
			do{
				newEndedBalance = newEndedBalance * (1 + (initInterest / 2));
				i = i+capPeriod;
			}while(i <= days);
			
			initInterest += 0.0001f;
		}
		
		if(reverseCalc){
			return (-1 * initInterest);
		}else{
			return initInterest;
		}
	}
	
	private double roundOff(double toRoundOff, int noOfDigitLeft){
		
		String[] tmpArr = Double.toString(toRoundOff).split("\\.");
		String fullPart = tmpArr[0];
		String decimalPart = tmpArr[1];
		
		if(decimalPart.length() <= noOfDigitLeft){
			return toRoundOff;
		}else{
			return Double.parseDouble(fullPart + "." + decimalPart.substring(0, noOfDigitLeft));
		}

	}

}
